package com.apexedu.eas.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.HashSet;
import java.util.Set;

import javax.annotation.Resource;

import org.hibernate.Session;
import org.hibernate.jdbc.Work;

import com.apexedu.eas.dao.IBaseDao;
import com.apexedu.eas.exception.EasException;
import com.apexedu.framework.dao.BaseDao;

import oracle.jdbc.internal.OracleTypes;

public class BasisDao<T, PK extends Serializable> implements IBaseDao<T, PK> {

	public static int IN = 1;
	public static int OUT = 2;

	@Resource
	protected BaseDao baseDao;

	@Resource
	public EasDBUtil easDBUtil;

	private Class<T> classType = null;

	public BasisDao() {
		Class clz = this.getClass();
		Type type = clz.getGenericSuperclass();
		if (type instanceof ParameterizedType) {
			ParameterizedType t = ((ParameterizedType) type);
			Type[] types = t.getActualTypeArguments();
			classType = (Class<T>) types[0];
		}
	}

	public Class<T> getClassType() {
		return classType;
	}

	@Override
	public PK add(T entity) {
		return (PK) baseDao.save(entity);
	}

	@Override
	public void delete(T entity) {
	}

	@Override
	public void modify(T entity) {
		baseDao.saveOrUpdate(entity);
	}

	// usr_jw.pro_yx_xs2gxxs0101

	public Object [] execProcQuery(final String procName, final Object[][] argsType, final Object[] args) {
		Session session = baseDao.getCurrentSession();
		final Object [] retObj=new Object [argsType.length] ;
		session.doWork(new Work() {
			@Override
			public void execute(Connection connection) throws SQLException {
				CallableStatement call = connection.prepareCall(procName);
				Set<Integer> set =new HashSet<Integer>();
				for (int j = 0; j < argsType.length; j++) {
					int index =Integer.valueOf(argsType[j][0].toString());
					set.add(index);
					setArgByOut(call, argsType[j][1],index );
				}
				int offset =0;
				for (int j = 0; j < args.length+offset; j++) {
					if(set.contains(j+1)) {offset+=1;continue;}
					setArgByIn(call, args[ j - offset], j+1);   // 总的记数 - 偏移量
				}
				/*for (int j = 0; j < argsType.length; j++) {
					if (Integer.valueOf(argsType[j][0].toString()) == IN) {
						setArgByIn(call, args[j], j+1);
					} else if (Integer.valueOf(argsType[j][0].toString()) == OUT) {
						setArgByOut(call, argsType[j][1], j+1);
					} else
						throw new EasException(-1, "存储过程参数必须为IN或者OUT类型");
				}*/
				//call.registerOutParameter(1, OracleTypes.NUMBER);
				//call.registerOutParameter(2, OracleTypes.VARCHAR);
				//call.setDouble(3, id);
				call.execute();
				//int a = call.getInt(1);
				//String b = call.getString(2);
				//Object [] retObj =new Object [argsType.length] ;
				for (int i = 0; i < argsType.length; i++) {
					retObj [i] = getOut(call, argsType[i][1], Integer.valueOf(argsType[i][0].toString()));
				}
				closeStatement(call);  //释放资源
				// 如果一定要传递ResultSet，应该使用RowSet
			}
		});
		return retObj;
	}

	/**
	 * 关闭ResultSet
	 * 
	 * @param rs
	 */
	public static void closeResultSet(ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 关闭Statement
	 * 
	 * @param st
	 */
	public static void closeStatement(Statement st) {
		if (st != null) {
			try {
				st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 关闭Connection
	 * 
	 * @param conn
	 */
	public static void closeConnection(Connection conn) {
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 关闭全部
	 * 
	 * @param rs
	 * @param sta
	 * @param conn
	 */
	public static void closeAll(ResultSet rs, Statement sta, Connection conn) {
		closeConnection(conn);
		closeStatement(sta);
		closeResultSet(rs);
	}

	public void setArgByIn(CallableStatement callableStatement, Object arg, int i) {

		try {
			if (arg == null)
				callableStatement.setNull(i, Types.NULL);
			if (arg instanceof String)
				callableStatement.setString(i, arg.toString());
			else if (arg instanceof Integer)
				callableStatement.setInt(i, ((Integer) arg).intValue());
			else if (arg instanceof Long)
				callableStatement.setLong(i, ((Long) arg).longValue());
			else if (arg instanceof Float)
				callableStatement.setFloat(i, ((Float) arg).floatValue());
			else if (arg instanceof Double)
				callableStatement.setDouble(i, ((Double) arg).doubleValue());
			else if (arg instanceof Boolean)
				callableStatement.setBoolean(i, ((Boolean) arg).booleanValue());
			else if (arg instanceof Array)
				callableStatement.setArray(i, (Array) arg);
			else if (arg instanceof byte[])
				callableStatement.setBytes(i, (byte[]) arg);
			else if (arg instanceof Date)
				callableStatement.setDate(i, (Date) arg);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public void setArgByOut(CallableStatement callableStatement, Object argType, int i) {
		try {
			if (argType == null) {
				callableStatement.registerOutParameter(i, Types.NULL);
			} else if (argType == String.class) {
				callableStatement.registerOutParameter(i, Types.VARCHAR);
			} else if (argType == Integer.class) {
				callableStatement.registerOutParameter(i, Types.INTEGER);
			} else if (argType == Long.class) {
				callableStatement.registerOutParameter(i, Types.INTEGER);
			} else if (argType == Float.class) {
				callableStatement.registerOutParameter(i, Types.FLOAT);
			} else if (argType == Double.class) {
				callableStatement.registerOutParameter(i, Types.DOUBLE);
			} else if (argType == Boolean.class) {
				callableStatement.registerOutParameter(i, Types.REAL);
			} else if (argType == Array.class) {
				callableStatement.registerOutParameter(i, Types.ARRAY);
			} else if (argType == byte[].class) {
				callableStatement.registerOutParameter(i, Types.ARRAY);
			} else if (argType == Date.class) {
				callableStatement.registerOutParameter(i, Types.DATE);
			} else
				callableStatement.registerOutParameter(i, Types.OTHER);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public Object getOut(CallableStatement callableStatement, Object argType, int i) {
		try {
			if (argType == null) {
				return null;
			}
			else if (argType == String.class) {
				return callableStatement.getString(i);
			}
			else if (argType == Integer.class) {
				return new Integer(callableStatement.getInt(i));
			}
			else if (argType == Long.class) {
				return new Long(callableStatement.getLong(i));
			}
			else if (argType == Float.class) {
				return new Float(callableStatement.getFloat(i));
			}
			else if (argType == Double.class) {
				return new Double(callableStatement.getDouble(i));
			}
			else if (argType == Boolean.class) {
				return new Boolean(callableStatement.getBoolean(i));
			}
			else if (argType == Array.class) {
				return callableStatement.getArray(i);
			}
			else if (argType == byte[].class) {
				return callableStatement.getBytes(i);
			}
			else if (argType == Date.class) {
				return callableStatement.getDate(i);
			}
			return callableStatement.getObject(i);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	public static void main(String[] a) {
		/*
		 * Object [] [] os ={{1,Double.class},{2,String.class}}; Object [] args
		 * ={1};
		 * 
		 * execProcQuery("{call usr_jw.pro_yx_xs2gxxs0101(?,?,?)}" ,os,args);
		 */
	}
}
